package top.lshaci.learning.nio.chat;

import cn.hutool.core.util.StrUtil;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * GroupChatServer
 *
 * @author lshaci
 * @since 1.0.0
 */
public class GroupChatServer {

    private final int port;

    private final Selector selector;

    private final ServerSocketChannel serverSocketChannel;

    public GroupChatServer(int port) throws IOException {
        this.port = port;
        this.selector = Selector.open();
        this.serverSocketChannel = ServerSocketChannel.open();
        this.serverSocketChannel.configureBlocking(false);
        this.serverSocketChannel.bind(new InetSocketAddress(port));

        this.serverSocketChannel.register(this.selector, SelectionKey.OP_ACCEPT);
    }

    public void start() throws IOException {
        System.err.println("群聊服务器启动在：" + this.port + " 端口");
        while (true) {
            final int count = this.selector.selectNow();
            if (count > 0) {
                final Set<SelectionKey> selectionKeys = selector.selectedKeys();
                final Iterator<SelectionKey> iterator = selectionKeys.iterator();
                while (iterator.hasNext()) {
                    final SelectionKey selectionKey = iterator.next();
                    if (selectionKey.isAcceptable()) {
                        final SocketChannel socketChannel = this.serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.err.println(socketChannel.getRemoteAddress() + " => 上线。");
                    }
                    if (selectionKey.isReadable()) {
                        read(selectionKey);
                    }
                    // 当前的 key 移除, 防止重复操作
                    iterator.remove();
                }
            }
        }
    }

    private void read(SelectionKey selectionKey) throws IOException {
        SocketChannel socketChannel = null;
        try {
            socketChannel = (SocketChannel) selectionKey.channel();
            final ByteBuffer byteBuffer = ByteBuffer.allocate(8192);
            final int read = socketChannel.read(byteBuffer);
            if (read > 0) {
                final String msg = StrUtil.str(byteBuffer.array(), Charset.defaultCharset());
                System.err.println(socketChannel.getRemoteAddress() + ": " + msg);
                // 发送给其它客户端
                send2Other(msg, socketChannel);
            }
        } catch (IOException e) {
            // 取消注册
            selectionKey.cancel();
            if (socketChannel != null) {
                System.err.println(socketChannel.getRemoteAddress() + ": 离线了。");
                // 关闭通道
                socketChannel.close();
            }
        }
    }

    /**
     * 转发给其它客户端
     *
     * @param msg  消息
     * @param self 自己
     * @throws IOException
     */
    private void send2Other(String msg, SocketChannel self) throws IOException {
        final Set<SelectionKey> keys = this.selector.keys();
        for (SelectionKey key : keys) {
            final SelectableChannel channel = key.channel();
            // 判断是否是 SocketChannel 和 自己
            if (channel instanceof SocketChannel && channel != self) {
                SocketChannel socketChannel = (SocketChannel) channel;
                final ByteBuffer byteBuffer = StrUtil.byteBuffer(msg, "UTF-8");
                socketChannel.write(byteBuffer);
                System.err.println("发送 =>" + socketChannel.getRemoteAddress() + ": " + msg);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        final GroupChatServer server = new GroupChatServer(6677);
        server.start();
    }
}
